using System;
using System.Collections.Generic;
using System.Text;
using Petra.Gis;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.DataServices.WKB.Support;
using System.IO;

namespace Petra.Gis.DataServices.WKB
{
  public partial class GeometryFactory
  {
    /***********************************************
     * 28.8.2007
     * 
     * creates GeometryCollection
     * from intergraph blob structure
     * 
     * asida@centrum.cz, initial implementation
     *************************************************/
    private static GeometryBase binaryToGeometryCollection(BinaryReader mBReader/*, int arrayOffset*/, System.Type parentGeomType)
    {

      GeometryCollection oGeom = new GeometryCollection();

      //long pos;
      int numGeoms;
      GeometryBase tmpGeom;

      //mBReader.BaseStream.Position = arrayOffset;

      //get endian
      WKB.ByteOrder dataEndian = (WKB.ByteOrder)mBReader.ReadByte();

      //skip geometry type, allready known here
      //mBReader.BaseStream.Position = arrayOffset + 5;
      mBReader.BaseStream.Position += 4;

      numGeoms = (int)EndianConverter.ToUInt32(mBReader.ReadUInt32(), dataEndian);
      //pos += 4;
      for (int i = 0; i < numGeoms; i++)
      {
        //this call modifies stream position, so program can recognize lenght of processed geometry !!
        //basestream = always MemoryStream
        tmpGeom = _createGeometry(mBReader/*, (int)mBReader.BaseStream.Position*/, typeof(GeometryCollection));

        oGeom.Add(tmpGeom);

        //pos = pos + tmpGeomItem.Length;
        //pos = mBReader.BaseStream.Position + 1;
      }

      //odlschool way
      //fixed (byte* bPtr = &vData[arrayOffset])
      //{
      //  GeometryBase tmpGeom1;

      //  //get num of geoms in collection
      //  pos = 5;
      //  int* _numGeoms = (Int32*)(bPtr + pos);
      //  numGeoms = EndianConverter.ToInt32(*_numGeoms, dataEndian);
      
      //  //step over all items
      //  //set pos to binary size of first geom
      //  pos += 4;
      //  byte[] tmpGeomItem;
      //  for (int i = 0; i < numGeoms; i++)
      //  {
      //    //lGeomSize = (Int32*)(bPtr + pos);
      //    tmpGeom1 = _createGeometry(mBReader, (arrayOffset + pos), typeof(GeometryCollection));

      //    //get geometry binary size by save it as wkb ;-)
      //    tmpGeomItem = saveGeometryBlob(tmpGeom);
      //    oGeom.Add(tmpGeom);
      //    pos = pos + tmpGeomItem.Length;
      //  }
      //}

      //try if collection contains single type of geometry
      //if yes then convert into specific collection MultiPoint, MultiLabel, MultiLine, MultiArea
      System.Type gt;
      bool bMultiPoint = false;
      bool bMultiLine = false;
      bool bMultiArea = false;

      foreach (GeometryBase g in oGeom)
      {
        gt = g.GetType();
        if (gt == typeof(Point))
          bMultiPoint = true;
        if (gt == typeof(LineString))
          bMultiLine = true;
        if (gt == typeof(Polygon))
          bMultiArea = true;
      }

      //lookup MultiPoint
      if (bMultiPoint && !(bMultiLine || bMultiArea))
      {
        MultiPoint outgeom = new MultiPoint();
        foreach (GeometryBase g in oGeom)
          outgeom.Add(g);
        return outgeom;
      }

      //lookup MultiLine
      if (bMultiLine && !(bMultiPoint || bMultiArea))
      {
        MultiLine outgeom = new MultiLine();
        foreach (GeometryBase g in oGeom)
          outgeom.Add(g);
        return outgeom;
      }

      //lookup MultiArea
      if (bMultiArea && !(bMultiPoint || bMultiLine))
      {
        MultiArea outgeom = new MultiArea();
        foreach (GeometryBase g in oGeom)
          outgeom.Add(g);
        return outgeom;
      }

      return oGeom;
    }




    /***********************************************
     * 28.8.2007
     * 
     * creates intergraph blob structure 
     * from GeometryCollection
     * 
     * asida@centrum.cz, initial implementation
     *************************************************/

    private static byte[] geometryCollectionToBinary(GeometryBase geometry)
    {
      //cast to Polygon first
      GeometryCollection _coll = (GeometryCollection)geometry;

      System.IO.MemoryStream oIMStream = new System.IO.MemoryStream();
      System.IO.BinaryWriter mBWriter = new System.IO.BinaryWriter(oIMStream);

      oIMStream.Position = 0;

      mBWriter.Write((byte)EndianConverter.GetCurrentPlatformEndian());
      mBWriter.Write((Int32)WKB.WKBGeometryTypes.GeometryCollection);

      mBWriter.Write(collectionItemsToBinary(_coll, 0));
      return oIMStream.ToArray();
    }





    /// <summary>
    /// collectionItemsToBinary takes geometry collection as input
    /// and converts all elements in collection from startIndex to binary
    /// startIndex is first element to process
    /// </summary>
    private static byte[] collectionItemsToBinary(GeometryCollection items, int startIndex)
    {

      /*
      if(startIndex >= items.Count || startIndex < 0)
        throw new ArgumentException("Index out of range collectionItemsToBinary()";
      */

      MemoryStream oIMStream = new MemoryStream();
      BinaryWriter mBWriter = new BinaryWriter(oIMStream);

      //write num of items
      mBWriter.Write((Int32)items.Count);

      byte[] tmp;
      for (int i = startIndex; i < items.Count; i++)
      {
        tmp = _saveGeometryBlob(items[i]);
        mBWriter.Write(tmp);
      }
      return oIMStream.ToArray();
    }
  }
}
